package com.rizzo.back.service.impl;

import com.rizzo.back.configuration.Configuration;
import com.rizzo.back.exception.ApplicationException;
import com.rizzo.back.service.ConfigurationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.xml.sax.SAXException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.File;
import java.io.IOException;
import java.net.UnknownHostException;

/**
 * Service to retrieve the {@link com.rizzo.back.configuration.Configuration} from the XML-file.
 * Therefore it is coupled with the gouse-configuration.xsd file. It provides various convenience-methods
 * for retrieving configured settings. All the public methods should be method-cached ofcourse.
 *
 * @author Jan Lievens
 */
public class ConfigurationContainer implements ConfigurationService {

    /** Field LOGGER  */
    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationContainer.class);

    /** Field instance  */
    private static ConfigurationContainer instance = new ConfigurationContainer();

    /** Field jndiConfigurationLocation  */
    private String jndiConfigurationLocation;

    /** Field jndiEnvironmentProperty  */
    private String jndiEnvironmentProperty;

    /** Field configuration  */
    private Configuration configuration;

    /** Field SCHEMA  */
    private final static String SCHEMA = "/be/ganymed/gouse/configuration/gouse-configuration.xsd";

    /**
     * Constructor ConfigurationContainer creates a new ConfigurationContainer instance.
     */
    private ConfigurationContainer() {
    }

    /**
     * Method init will be performed while bootstrapping so different methods for retrieving configuration-data
     * are tried here so the application will not start if anything fails in this method.
     */
    public void init() {
        try {
            File configurationFile = new File(this.jndiConfigurationLocation);
            JAXBContext jaxbContext = JAXBContext.newInstance(Configuration.class);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            SchemaFactory schemaFactory = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
            Schema schema = schemaFactory.newSchema(new ClassPathResource(SCHEMA).getURL());
            unmarshaller.setSchema(schema);
            this.configuration = (Configuration) unmarshaller.unmarshal(configurationFile);
            // we must run these methods in the init-method because at runtime we don't want exceptions from them
        } catch (UnknownHostException e) {
            LOGGER.warn("Hostname could not be retrieved from InetAddress. This can have its implications on the configuration of the application.");
            throw new RuntimeException(e);
        } catch (JAXBException e) {
            LOGGER.error("Error while parsing the configuration.xml file. This has its implications on the configuration of the application.");
            throw new RuntimeException(e);
        } catch (IOException e) {
            LOGGER.error("Error while accessing the configuration.xml file. This has its implications on the configuration of the application.");
            throw new RuntimeException(e);
        } catch (SAXException e) {
            LOGGER.error("Error while parsing the configuration.xml file. This has its implications on the configuration of the application.");
            throw new RuntimeException(e);
        }
        LOGGER.info("ConfigurationContainer successfully initialized.");
    }

    /**
     * Method getInstance returns the instance of this ConfigurationContainer object.
     *
     * @return the instance (type ConfigurationContainer) of this ConfigurationContainer object.
     */
    public static ConfigurationContainer getInstance() {
        return instance;
    }

    /**
     * @see com.rizzo.back.service.ConfigurationService#getConfiguration() 
     */
    public Configuration getConfiguration() throws ApplicationException {
        return this.configuration;
    }

    /**
     * Method setJndiConfigurationLocation sets the jndiConfigurationLocation of this ConfigurationContainer object
     *
     * @param jndiConfigurationLocation of type String
     */
    public void setConfigurationLocation(String jndiConfigurationLocation) {
        this.jndiConfigurationLocation = jndiConfigurationLocation;
    }

    /**
     * Method setJndiEnvironmentProperty sets the jndiEnvironmentProperty of this ConfigurationContainer object
     *
     * @param jndiEnvironmentProperty of type String
     */
    public void setEnvironmentProperty(String jndiEnvironmentProperty) {
        this.jndiEnvironmentProperty = jndiEnvironmentProperty;
    }

}
